﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.IO;
using Microsoft.Xna.Framework.Storage;
using System.Xml.Serialization;

namespace Project
{
    /// <summary>
    /// This is the main menu
    /// </summary>
    /// 
    //lots o' stuff from http://www.alecjacobson.com/weblog/?p=539


    public class LevelSelect : EngineState
    {
        Engine engine;
        ContentManager content;
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont statsFont, levelFont;
        int[] highScores;

        //mouse pressed and mouse just pressed
        bool mpressed, prev_mpressed = false;
        //mouse location in window
        int mx, my;
        double frame_time;

        enum BState
        {
            HOVER,
            UP,
            JUST_RELEASED,
            DOWN
        }
        const int NUMBER_OF_BUTTONS = 5,
            BACK_ARROW_INDEX = 0,
            FORWARD_ARROW_INDEX = 1,
            LEVEL1_INDEX = 2,
            LEVEL2_INDEX = 3,
            LEVEL3_INDEX = 4;

        Rectangle[] button_rectangle = new Rectangle[NUMBER_OF_BUTTONS];
        BState[] button_state = new BState[NUMBER_OF_BUTTONS];
        Texture2D[] button_texture = new Texture2D[NUMBER_OF_BUTTONS];
        double[] button_timer = new double[NUMBER_OF_BUTTONS];

        const int NUMBER_OF_LEVELS = 4,
            LEVEL_SELECT_HEIGHT = 300,
            LEVEL_SELECT_WIDTH = 200;
        Color background_color;
        Rectangle[] level_rectangle = new Rectangle[NUMBER_OF_LEVELS];
        BState[] level_state = new BState[NUMBER_OF_LEVELS];
        Texture2D[] level_thumbnail = new Texture2D[NUMBER_OF_LEVELS];
        double[] level_timer = new double[NUMBER_OF_LEVELS];

        Texture2D backArrow, forwardArrow, levelSelect, check, checkplus, star;

        int levelNum;


        public void initialize(Engine e, ContentManager cm, GraphicsDeviceManager gdm)
        {
            engine = e;
            content = cm;
            graphics = gdm;

            highScores = this.LoadHighScores("highscores.lst");

            Vector2 viewportSize = new Vector2(graphics.GraphicsDevice.Viewport.Width,
                graphics.GraphicsDevice.Viewport.Height);
            // starting x and y locations to stack buttons
            // vertically in the middle of the screen
            //int x = graphics.GraphicsDevice.Viewport.Width / 2 - BUTTON_WIDTH / 2;
            //int y = graphics.GraphicsDevice.Viewport.Height / 2 -
            //    NUMBER_OF_BUTTONS / 2 * BUTTON_HEIGHT -
            //    (NUMBER_OF_BUTTONS % 2) * BUTTON_HEIGHT / 2 + 20;

            levelNum = 1; //Starting level
            button_rectangle[LEVEL1_INDEX] = new Rectangle(75, 80, 200, 300);
            button_rectangle[LEVEL2_INDEX] = new Rectangle(300, 80, 200, 300);
            button_rectangle[LEVEL3_INDEX] = new Rectangle(525, 80, 200, 300);
            button_rectangle[BACK_ARROW_INDEX] = new Rectangle(10, 175, 50, 50);
            button_rectangle[FORWARD_ARROW_INDEX] = new Rectangle(740, 175, 50, 50);

            button_texture[LEVEL1_INDEX] = levelSelect;
            button_texture[LEVEL2_INDEX] = levelSelect;
            button_texture[LEVEL3_INDEX] = levelSelect;
            button_texture[BACK_ARROW_INDEX] = backArrow;
            button_texture[FORWARD_ARROW_INDEX] = forwardArrow;

            for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
            {
                button_state[i] = BState.UP;
                button_timer[i] = 0.0;
            }

            engine.setMouseVisible(true);
            background_color = new Color(30,30,30);
        }

        public void destroy()
        {
        }

        public void loadContent()
        {
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
            statsFont = content.Load<SpriteFont>("fonts/Arial");
            levelFont = content.Load<SpriteFont>("fonts/BigArial");

            button_texture[LEVEL1_INDEX] = content.Load<Texture2D>(@"images/level_select"); ;
            button_texture[LEVEL2_INDEX] = content.Load<Texture2D>(@"images/level_select"); ;
            button_texture[LEVEL3_INDEX] = content.Load<Texture2D>(@"images/level_select"); ;
            button_texture[BACK_ARROW_INDEX] = content.Load<Texture2D>(@"images/arrow_back");
            button_texture[FORWARD_ARROW_INDEX] = content.Load<Texture2D>(@"images/arrow_forwards");

            levelSelect = content.Load<Texture2D>(@"images/level_select");
            backArrow = content.Load<Texture2D>(@"images/arrow_back");
            forwardArrow = content.Load<Texture2D>(@"images/arrow_forwards");

            star = content.Load<Texture2D>(@"images/star");
            checkplus = content.Load<Texture2D>(@"images/check_plus");
            check = content.Load<Texture2D>(@"images/check");

            level_thumbnail[0] = content.Load<Texture2D>(@"images/level1");
            level_thumbnail[1] = content.Load<Texture2D>(@"images/level2");
            level_thumbnail[2] = content.Load<Texture2D>(@"images/level3");
        }

        public void update(GameTime gameTime)
        {
            if ((Keyboard.GetState().IsKeyDown(Keys.Escape)))
                engine.exit();
            else
            {
                // get elapsed frame time in seconds
                frame_time = gameTime.ElapsedGameTime.Milliseconds / 1000.0;

                // update mouse variables
                MouseState mouse_state = Mouse.GetState();
                mx = mouse_state.X;
                my = mouse_state.Y;
                prev_mpressed = mpressed;
                mpressed = mouse_state.LeftButton == ButtonState.Pressed;
            }
            update_buttons();
        }

        public void draw(GameTime gameTime)
        {

            graphics.GraphicsDevice.Clear(background_color);

            spriteBatch.Begin();

            //Draw Arrows
            spriteBatch.Draw(backArrow, button_rectangle[BACK_ARROW_INDEX], Color.White);
            spriteBatch.Draw(forwardArrow, button_rectangle[FORWARD_ARROW_INDEX], Color.White);

            //Draw Level Selectors
            spriteBatch.Draw(levelSelect, button_rectangle[LEVEL1_INDEX], Color.White);
            spriteBatch.Draw(levelSelect, button_rectangle[LEVEL2_INDEX], Color.White);
            spriteBatch.Draw(levelSelect, button_rectangle[LEVEL3_INDEX], Color.White);

            for (int i = 0; i < 3; i++)
            {
                if (level_thumbnail[levelNum + i - 1] != null)
                {
                    spriteBatch.Draw(level_thumbnail[levelNum + i - 1], new Rectangle(95+(i*225), 96, 160, 160), Color.White);
                }
            }

            //Draw level numbers
            spriteBatch.DrawString(levelFont, levelNum.ToString(), new Vector2(90, 270), Color.White);
            spriteBatch.DrawString(levelFont, (levelNum + 1).ToString(), new Vector2(320, 270), Color.White);
            spriteBatch.DrawString(levelFont, (levelNum + 2).ToString(), new Vector2(540, 270), Color.White);

            //Draw best time
            spriteBatch.DrawString(levelFont, toTime(highScores[levelNum-1].ToString()), new Vector2(90, 325), Color.White);
            spriteBatch.DrawString(levelFont, toTime(highScores[levelNum].ToString()), new Vector2(320, 325), Color.White);
            spriteBatch.DrawString(levelFont, toTime(highScores[levelNum+1].ToString()), new Vector2(540, 325), Color.White);

            //Draw check/plus/star
            spriteBatch.Draw(goalImage(highScores[levelNum-1]), new Rectangle(200, 270, 50, 50), Color.White);
            spriteBatch.Draw(goalImage(highScores[levelNum]), new Rectangle(430, 270, 50, 50), Color.White);
            spriteBatch.Draw(goalImage(highScores[levelNum+1]), new Rectangle(650, 270, 50, 50), Color.White);

            spriteBatch.End();
        }

        private Texture2D goalImage(int time)
        {
            if (time <= 300)
                return star;
            else if (time <= 600)
                return checkplus;
            else
                return check;
        }

        // wrapper for hit_image_alpha taking Rectangle and Texture
        Boolean hit_image_alpha(Rectangle rect, Texture2D tex, int x, int y)
        {
            return hit_image_alpha(0, 0, tex, tex.Width * (x - rect.X) /
                rect.Width, tex.Height * (y - rect.Y) / rect.Height);
        }

        // wraps hit_image then determines if hit a transparent part of image
        Boolean hit_image_alpha(float tx, float ty, Texture2D tex, int x, int y)
        {
            if (hit_image(tx, ty, tex, x, y))
            {
                uint[] data = new uint[tex.Width * tex.Height];
                tex.GetData<uint>(data);
                if ((x - (int)tx) + (y - (int)ty) *
                    tex.Width < tex.Width * tex.Height)
                {
                    return ((data[
                        (x - (int)tx) + (y - (int)ty) * tex.Width
                        ] &
                                0xFF000000) >> 24) > 20;
                }
            }
            return false;
        }

        // determine if x,y is within rectangle formed by texture located at tx,ty
        Boolean hit_image(float tx, float ty, Texture2D tex, int x, int y)
        {
            return (x >= tx &&
                x <= tx + tex.Width &&
                y >= ty &&
                y <= ty + tex.Height);
        }

        // determine state and color of button
        void update_buttons()
        {
            for (int i = 0; i < NUMBER_OF_BUTTONS; i++)
            {

                if (hit_image_alpha(
                    button_rectangle[i], button_texture[i], mx, my))
                {
                    //button_texture[i] = button_hover_texture[i];
                    button_timer[i] = 0.0;
                    if (mpressed)
                    {
                        // mouse is currently down
                        button_state[i] = BState.DOWN;
                        //button_color[i] = Color.Blue;
                    }
                    else if (!mpressed && prev_mpressed)
                    {
                        // mouse was just released
                        if (button_state[i] == BState.DOWN)
                        {
                            // button i was just down
                            button_state[i] = BState.JUST_RELEASED;
                        }
                    }
                    else
                    {
                        button_state[i] = BState.HOVER;
                        //button_color[i] = Color.LightBlue;
                    }
                }
                else
                {
                    button_state[i] = BState.UP;
                    if (button_timer[i] > 0)
                    {
                        button_timer[i] = button_timer[i] - frame_time;
                    }
                }

                if (button_state[i] == BState.JUST_RELEASED)
                {
                    take_action_on_button(i);
                }
            }
        }

        // Logic for each button click goes here
        void take_action_on_button(int i)
        {
            MediaPlayer.Stop();
            //take action corresponding to which button was clicked
            switch (i)
            {
                case LEVEL1_INDEX:
                    engine.setState(new Game(1)); //Change this to levelnum's level
                    break;
                case LEVEL2_INDEX:
                    engine.setState(new Game(2)); //Change this to levelnum+1's level
                    break;
                case LEVEL3_INDEX:
                    engine.setState(new Game(3)); //Change this to levelnum+2's level
                    break;
                case BACK_ARROW_INDEX:
                    if (levelNum > 1)
                        levelNum -= 1;
                    break;
                case FORWARD_ARROW_INDEX:
                    if (levelNum <= NUMBER_OF_LEVELS-3)
                        levelNum += 1;
                    break;
                default:
                    break;
            }

        }

        private String toTime(String s)
        {
            s = s.PadLeft(4, '0');
            s = s.Insert(2, ":");
            return s;
        }

        //From XNA essentials
        public int[] LoadHighScores(string filename)
        {
            int[] data;

            // Open the file
            FileStream stream = File.Open(filename, FileMode.OpenOrCreate,
            FileAccess.Read);
            try
            {
                // Read the data from the file
                XmlSerializer serializer = new XmlSerializer(typeof(int[]));
                data = (int[])serializer.Deserialize(stream);
            }
            finally
            {
                // Close the file
                stream.Close();
            }

            return (data);
        }
    }
}
